\subsubsection{ARM: \OptimizingXcodeIV (\ARMMode)}

Пока в ARM не было стандартного набора инструкций для работы с числами с плавающей точкой, разные производители процессоров
могли добавлять свои расширения для работы с ними.
Позже был принят стандарт VFP (\IT{Vector Floating Point}).

Важное отличие от x86 в том, что там вы работаете с FPU-стеком, а здесь стека нет, вы работаете просто с регистрами.

\lstinputlisting[label=ARM_leaf_example10,caption=\OptimizingXcodeIV (\ARMMode),style=customasmARM]{patterns/12_FPU/1_simple/ARM/Xcode_ARM_O3_RU.asm}

\myindex{ARM!D-\registers{}}
\myindex{ARM!S-\registers{}}
Итак, здесь мы видим использование новых регистров с префиксом D.

Это 64-битные регистры. Их 32 и их можно
использовать для чисел с плавающей точкой двойной точности (double) и для 
SIMD (в ARM это называется NEON).

Имеются также 32 32-битных S-регистра. Они применяются для работы с числами 
с плавающей точкой одинарной точности (float).

Запомнить легко: D-регистры предназначены для чисел double-точности, 
а S-регистры~--- для чисел single-точности.

Больше об этом: \myref{ARM_VFP_registers}.

Обе константы (3,14 и 4,1) хранятся в памяти в формате IEEE 754.

\myindex{ARM!\Instructions!VLDR}
\myindex{ARM!\Instructions!VMOV}
Инструкции \INS{VLDR} и \INS{VMOV}, как можно догадаться, это аналоги обычных \INS{LDR} и \MOV, но они работают с D-регистрами.

Важно отметить, что эти инструкции, как и D-регистры, предназначены не только для работы 
с числами с плавающей точкой, но пригодны также и для работы с SIMD (NEON), и позже это также будет видно.

Аргументы передаются в функцию обычным путем через R-регистры, однако 
каждое число, имеющее двойную точность, занимает 64 бита, так что для передачи каждого нужны два R-регистра.

\INS{VMOV D17, R0, R1} в самом начале составляет два 32-битных значения из \Reg{0} и \Reg{1} в одно 64-битное и сохраняет в \GTT{D17}.

\INS{VMOV R0, R1, D16} в конце это обратная процедура: то что было в \GTT{D16} 
остается в двух регистрах \Reg{0} и \Reg{1}, потому что число с двойной точностью, 
занимающее 64 бита, возвращается в паре регистров \Reg{0} и \Reg{1}.

\myindex{ARM!\Instructions!VDIV}
\myindex{ARM!\Instructions!VMUL}
\myindex{ARM!\Instructions!VADD}
\INS{VDIV}, \INS{VMUL} и \INS{VADD}, это инструкции для работы с числами 
с плавающей точкой, вычисляющие, соответственно, \glslink{quotient}{частное}, \glslink{product}{произведение} и сумму.

Код для Thumb-2 такой же.

\subsubsection{ARM: \OptimizingKeilVI (\ThumbMode)}

\lstinputlisting[style=customasmARM]{patterns/12_FPU/1_simple/ARM/Keil_O3_thumb_RU.asm}

Keil компилировал для процессора, в котором может и не быть поддержки FPU или NEON.
Так что числа с двойной точностью передаются в парах обычных R-регистров,
а вместо FPU-инструкций вызываются сервисные библиотечные функции\\
\GTT{\_\_aeabi\_dmul}, \GTT{\_\_aeabi\_ddiv}, \GTT{\_\_aeabi\_dadd}, эмулирующие умножение, деление и сложение чисел с плавающей точкой.

Конечно, это медленнее чем FPU-сопроцессор, но это лучше, чем ничего.

Кстати, похожие библиотеки для эмуляции сопроцессорных инструкций были очень распространены в x86 
когда сопроцессор был редким и дорогим и присутствовал далеко не во всех компьютерах.

\myindex{ARM!soft float}
\myindex{ARM!armel}
\myindex{ARM!armhf}
\myindex{ARM!hard float}
Эмуляция FPU-сопроцессора в ARM называется \IT{soft float} или \IT{armel} (\IT{emulation}),
а использование FPU-инструкций сопроцессора~--- \IT{hard float} или \IT{armhf}.

\iffalse
% TODO разобраться...
\myindex{Raspberry Pi}
Ядро Linux, например, для Raspberry Pi может поставляться в двух вариантах.

В случае \IT{soft float}, аргументы будут передаваться через R-регистры, 
а в случае \IT{hard float}, через D-регистры.


И это то, что помешает использовать, например, armhf-библиотеки
из armel-кода или наоборот, поэтому, весь код в дистрибутиве Linux должен быть скомпилирован
в соответствии с выбранным соглашением о вызовах.

\fi

\subsubsection{ARM64: \Optimizing GCC (Linaro) 4.9}

Очень компактный код:

\lstinputlisting[caption=\Optimizing GCC (Linaro) 4.9,style=customasmARM]{patterns/12_FPU/1_simple/ARM/ARM64_GCC_O3_RU.s}

\subsubsection{ARM64: \NonOptimizing GCC (Linaro) 4.9}

\lstinputlisting[caption=\NonOptimizing GCC (Linaro) 4.9,style=customasmARM]{patterns/12_FPU/1_simple/ARM/ARM64_GCC_O0_RU.s}

\NonOptimizing GCC более многословный.
Здесь много ненужных перетасовок значений, включая явно избыточный код 
(последние две инструкции \INS{GMOV}).
Должно быть, GCC 4.9 пока ещё не очень хорош для генерации кода под ARM64.
Интересно заметить что у ARM64 64-битные регистры и D-регистры так же 64-битные.
Так что компилятор может сохранять значения типа \Tdouble в \ac{GPR} вместо локального стека.
Это было невозможно на 32-битных CPU.
И снова, как упражнение, вы можете попробовать соптимизировать эту функцию вручную, без добавления
новых инструкций вроде \INS{FMADD}.

